
#include "Base.h"

#if defined(PLATFORM_ANDROID)

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>

#else

#include <GL/glew.h>
#include <gl/gl.h>
#include "Extensions/glext.h"

#endif // #if defined(PLATFORM_ANDROID)

#include "OGLSpriteBatch.h"
#include "Maths/Vector2.h"
#include "Graphics/Textures/Texture.h"
#include "Maths\Rectangle.h"
#include "Graphics/SpriteTypes.h"
#include "Graphics/Renderer/Renderer.h"

#include "OGLMacro.h"
#include "OGLTexture.h"
#include "Maths\Math.h"

#include "Memory/DebugNew.h"
#include "../../Vertices/VertexType.h"
#include "Log/LogManager.h"

namespace CasaEngine
{
	/**
	 *
	 */
	OGLSpriteBatch::OGLSpriteBatch(const IRenderer* renderer_) :
		m_pRenderer(renderer_),
		m_pBufferVertex(NULL),
		m_pBufferPosition(NULL),
		m_pBufferColor(NULL),
		m_MaxSprite(1000),
		m_SpriteDatas(100),
		m_SpriteTextures(1000),
		m_NbElt(0)
	{
		m_pPositionData = new float[m_MaxSprite * 4];
		m_pColorData = new unsigned long[m_MaxSprite];

		const DeclarationElement::TDeclarationElement elements[] =
		{
			{0, DeclarationElement::Position,  DeclarationElement::Float3},
			{1, DeclarationElement::Normal,    DeclarationElement::Float4}, //position + size
			{2, DeclarationElement::Diffuse,   DeclarationElement::Color}
		};

		//m_Declaration = Renderer.CreateDeclaration(elements, 3);
		m_Declaration = Renderer.CreateDeclaration(VertexPositionColorTexture::Elements, VertexPositionColorTexture::NbElements);

		const float vertex_buffer_data[] = { 
			-0.5f, -0.5f, 0.0f,
			0.5f, -0.5f, 0.0f,
			-0.5f,  0.5f, 0.0f,
			0.5f,  0.5f, 0.0f,
		};
		m_pBufferVertex = m_pRenderer->CreateVertexBuffer(sizeof(vertex_buffer_data), BUF_STATIC, &vertex_buffer_data[0]);
		//position + size
		m_pBufferPosition = m_pRenderer->CreateVertexBuffer<float>(m_MaxSprite * 4 * sizeof(float), BUF_STREAM, NULL);
		m_pBufferColor = m_pRenderer->CreateVertexBuffer<unsigned long>(m_MaxSprite * sizeof(unsigned long), BUF_STREAM, NULL);

		m_pBufferSpriteVertex = m_pRenderer->CreateVertexBuffer<VertexPositionColorTexture>(m_MaxSprite * sizeof(VertexPositionColorTexture), BUF_DYNAMIC);

		short *pIndices = new short[m_MaxSprite * 6];

		for (int i=0; i<m_MaxSprite; i++)
		{
			pIndices[i + 0] = 0 + i * 4;
			pIndices[i + 1] = 3 + i * 4;
			pIndices[i + 2] = 1 + i * 4;
			pIndices[i + 3] = 1 + i * 4;
			pIndices[i + 4] = 3 + i * 4;
			pIndices[i + 5] = 2 + i * 4;
		}

		m_pBufferSpriteIndex = m_pRenderer->CreateIndexBuffer<short>(m_MaxSprite * 6, BUF_STATIC, pIndices);
		delete[] pIndices;

		m_pColorData = new unsigned long[m_MaxSprite];

		m_pEffect = Renderer.CreateEffectFromFile("SpriteBatch");
	}

	/**
	 * 
	 */
	OGLSpriteBatch::~OGLSpriteBatch()
	{
		delete[] m_pPositionData;
		delete[] m_pColorData;
	}

	/**
	 *
	 */
	void OGLSpriteBatch::Begin(int flags)
	{
		/*
		GLint iViewport[4];

		GLCheck(glGetIntegerv(GL_VIEWPORT, iViewport ));
		GLCheck(glMatrixMode(GL_PROJECTION));
		GLCheck(glPushMatrix());
		GLCheck(glLoadIdentity());
		GLCheck(glOrtho( iViewport[0], iViewport[0]+iViewport[2],
			iViewport[1]+iViewport[3], iViewport[1], -1, 1 ));

		GLCheck(glDisable(GL_DEPTH_TEST));
		GLCheck(glDisable(GL_LIGHTING));
		GLCheck(glEnable(GL_TEXTURE_2D));
		Renderer.SetupAlphaBlending(Blend::SrcAlpha, Blend::InvSrcAlpha);
		Renderer.SetupTextureUnit(0, TextureOperation::Modulate, TXA_TEXTURE, TXA_DIFFUSE);
		Renderer.SetupTextureUnit(0, TextureOperation::AlphaModulate, TXA_TEXTURE, TXA_DIFFUSE);
		Renderer.Enable(RenderParameter::AlphaBlend, true);
		Renderer.Enable(RenderParameter::ZWrite, false);*/
	}

	/**
	 * 
	 */
	void OGLSpriteBatch::End()
	{
		if (m_NbElt == 0)
		{
			return;
		}

		//m_pBufferPosition.Fill(m_pPositionData, m_NbElt * 4);
		//m_pBufferColor.Fill(m_pColorData, m_NbElt);
		// 
		m_pBufferSpriteVertex.Fill(&m_SpriteDatas[0], m_NbElt);

		Renderer.SetDeclaration(m_Declaration);

		//Renderer.SetVertexBuffer(0, m_pBufferVertex);
		//Renderer.SetVertexBuffer(1, m_pBufferPosition);
		//Renderer.SetVertexBuffer(2, m_pBufferColor);

		Renderer.SetVertexBuffer(0, m_pBufferSpriteVertex);
		Renderer.SetIndexBuffer(m_pBufferSpriteIndex);

		Renderer.SetTexture(0, m_SpriteTextures[0]);

		// These functions are specific to glDrawArrays*Instanced*.
		// The first parameter is the attribute buffer we're talking about.
		// The second parameter is the "rate at which generic vertex attributes advance when rendering multiple instances"
		// http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribDivisor.xml
// 		GLCheck(glVertexAttribDivisor(0, 0)); // particles vertices : always reuse the same 4 vertices -> 0
// 		GLCheck(glVertexAttribDivisor(1, 1)); // positions : one per quad (its center)                 -> 1
// 		GLCheck(glVertexAttribDivisor(2, 1)); // color : one per quad                                  -> 1

		std::vector<IEffectPass *>::iterator it;

		Matrix4 mat;
		mat.OrthoOffCenter(0,0,800,600);

		m_pEffect->Begin();

		for (it = m_pEffect->PassesBeginIterator();
			it != m_pEffect->PassesEndIterator();
			it++)
		{
			(*it)->Begin();
			(*it)->GetShader()->SetParameter("ModelViewProj", mat); //pCamera->GetViewMatrix() * pCamera->GetProjectionMatrix());

			//Renderer.DrawPrimitivesInstanced(PrimitiveType::TriangleList, 0, 4, m_NbElt);
			Renderer.DrawIndexedPrimitives(PrimitiveType::TriangleList, 0, m_NbElt * 2);
			//Renderer.DrawPrimitives(PrimitiveType::TriangleStrip, 0, m_NbElt * 2);

			(*it)->End();
		}

		m_pEffect->End();

		m_NbElt = 0;
	}

	/**
	 * 
	 */
	void OGLSpriteBatch::Draw(
		ITextureBase *Texture2D,
		CRectangle &PositionInTexture,
		Vector2I &Origin,
		Vector2F &Position,
		float Rotation,
		Vector2F &Scale,
		CColor &Color,
		float ZOrder,
		eSpriteEffects SpriteEffect)
	{
		if (m_NbElt >= m_MaxSprite)
		{
			LogManager::Instance().Log(LogVerbosity::Error, "Spritebatch : number of sprite exceed (%d)\n", m_MaxSprite);
			return;
		}

		/*m_pPositionData[m_NbElt * 4 + 0] = Position.x - Origin.x;
		m_pPositionData[m_NbElt * 4 + 1] = Position.y - Origin.y;
		m_pPositionData[m_NbElt * 4 + 2] = ZOrder;
		m_pPositionData[m_NbElt * 4 + 3] = Scale.x; // TODO : Scale.y
		m_pColorData[m_NbElt] = Renderer.ConvertColor(Color);*/

		m_SpriteTextures[m_NbElt] = Texture2D;

		//VertexPositionColorTexture *pBuffer = m_pBufferSpriteVertex.Lock(0, 0, LOCK_WRITEONLY);
		unsigned long color = Renderer.ConvertColor(Color);
		int widthBy2 = PositionInTexture.Width() / 2;
		int heightBy2 = PositionInTexture.Height() / 2;

		//Left-Top
		m_SpriteDatas[m_NbElt * 4 + 0].Position.x = Position.x - Origin.x - widthBy2;
		m_SpriteDatas[m_NbElt * 4 + 0].Position.y = Position.y - Origin.y - heightBy2;
		m_SpriteDatas[m_NbElt * 4 + 0].Position.z = ZOrder;
		m_SpriteDatas[m_NbElt * 4 + 0].Color = color;
		m_SpriteDatas[m_NbElt * 4 + 0].TexCoords.x = PositionInTexture.Left();
		m_SpriteDatas[m_NbElt * 4 + 0].TexCoords.y = PositionInTexture.Top();
		//Right-Top
		m_SpriteDatas[m_NbElt * 4 + 1].Position.x = Position.x - Origin.x + widthBy2;
		m_SpriteDatas[m_NbElt * 4 + 1].Position.y = Position.y - Origin.y - heightBy2;
		m_SpriteDatas[m_NbElt * 4 + 1].Position.z = ZOrder;
		m_SpriteDatas[m_NbElt * 4 + 1].Color = color;
		m_SpriteDatas[m_NbElt * 4 + 1].TexCoords.x = PositionInTexture.Right();
		m_SpriteDatas[m_NbElt * 4 + 1].TexCoords.y = PositionInTexture.Top();
		//Right-Bottom
		m_SpriteDatas[m_NbElt * 4 + 2].Position.x = Position.x - Origin.x + widthBy2;
		m_SpriteDatas[m_NbElt * 4 + 2].Position.y = Position.y - Origin.y + heightBy2;
		m_SpriteDatas[m_NbElt * 4 + 2].Position.z = ZOrder;
		m_SpriteDatas[m_NbElt * 4 + 2].Color = color;
		m_SpriteDatas[m_NbElt * 4 + 2].TexCoords.x = PositionInTexture.Right();
		m_SpriteDatas[m_NbElt * 4 + 2].TexCoords.y = PositionInTexture.Bottom();
		//Left-Bottom
		m_SpriteDatas[m_NbElt * 4 + 3].Position.x = Position.x - Origin.x - widthBy2;
		m_SpriteDatas[m_NbElt * 4 + 3].Position.y = Position.y - Origin.y + heightBy2;
		m_SpriteDatas[m_NbElt * 4 + 3].Position.z = ZOrder;
		m_SpriteDatas[m_NbElt * 4 + 3].Color = color;
		m_SpriteDatas[m_NbElt * 4 + 3].TexCoords.x = PositionInTexture.Left();
		m_SpriteDatas[m_NbElt * 4 + 3].TexCoords.y = PositionInTexture.Top();

		//m_pBufferSpriteVertex.Unlock();

		m_NbElt++;

		/*COGLTexture *pTexture = dynamic_cast<COGLTexture *>(Texture2D);

		if (pTexture == NULL)
		{
			throw new CArgumentException("OGLSpriteBatch::Draw() : ITextureBase is not a COGLTexture object");
		}		

		GLCheck(glMatrixMode(GL_MODELVIEW));
		GLCheck(glPushMatrix());
		GLCheck(glLoadIdentity());
		GLCheck(glScalef(Scale.x, Scale.y, 0.0f));
		GLCheck(glTranslated(Position.x, Position.y, 0));
		GLCheck(glRotatef(MATH_ToDegree(Rotation), 0.0f, 0.0f, 1.0f));
		GLCheck(glTranslated(-Origin.x, -Origin.y, 0));		

		GLCheck(glBindTexture(GL_TEXTURE_2D, pTexture->GetOGLTexture()));

		GLCheck(glColor4ub(Color.GetRed(), Color.GetGreen(), Color.GetBlue(), Color.GetAlpha()));

		glBegin(GL_QUADS);

			glTexCoord2f((GLfloat)PositionInTexture.Left() / (GLfloat)pTexture->GetSize().x,
				(GLfloat)PositionInTexture.Top() / (GLfloat)pTexture->GetSize().y);
			glVertex2f((GLfloat)PositionInTexture.Left(), (GLfloat)PositionInTexture.Top());

			glTexCoord2f((GLfloat)PositionInTexture.Right() / (GLfloat)pTexture->GetSize().x,
				(GLfloat)PositionInTexture.Top() / (GLfloat)pTexture->GetSize().y);
			glVertex2f((GLfloat)PositionInTexture.Right(), (GLfloat)PositionInTexture.Top());

			glTexCoord2f((GLfloat)PositionInTexture.Right() / (GLfloat)pTexture->GetSize().x,
				(GLfloat)PositionInTexture.Bottom() / (GLfloat)pTexture->GetSize().y);
			glVertex2f((GLfloat)PositionInTexture.Right(), (GLfloat)PositionInTexture.Bottom());

			glTexCoord2f((GLfloat)PositionInTexture.Left() / (GLfloat)pTexture->GetSize().x,
				(GLfloat)PositionInTexture.Bottom() / (GLfloat)pTexture->GetSize().y);
			glVertex2f((GLfloat)PositionInTexture.Left(), (GLfloat)PositionInTexture.Bottom()); 

		GLCheck(glEnd());*/
	}

} // namespace CasaEngine
